﻿using System;
using System.Collections.Generic;
using System.Text;
using Quartz;
using Quartz.Listener;
using log4net;
using System.IO;
using ShootSearch.Util;
using System.Diagnostics;
using log4net.Appender;
using System.Threading;
using ShootSearch.Logging;

namespace ShootSearch.Core
{
    public class Task
    {
        public const string JOB_CONTEXT_PATH = "ContextPath";
        public const string JOB_CONFIG = "Config";
        public const string JOB_NEXT = "Next";
        public const string JOB_LAUNCHNEXT = "LaunchNext";
        public const string TAKS_CONFIG = "Task.config";
        public const string TAKS_ENTITY = "Task";
        public const string TAKS_LOGGER = "Logger";
        public const string TASK_STATUS_FORMAT = "Status: {0} Start: {1} Running Jobs: {2}";

        protected Quartz.ISchedulerFactory schedulerFactory = new Quartz.Impl.StdSchedulerFactory();
        protected Quartz.IScheduler scheduler = null;
        protected TaskConfig config;
        protected ILog logger = null;
        protected bool hasHandledLog = false;
        protected StringBuilder sbLog = new StringBuilder();
        protected DateTime startTime = new DateTime();
        protected JobStatus status = JobStatus.Unstarted;

        public event EventHandler StatusChanged;
        public event MessageLoggedEventHandler MessageLogged;

        public Dictionary<ISpiderMan, int> runningJobs;

        public JobStatus Status
        {
            get { return status; }
            protected set
            {
                status = value;
                if (StatusChanged != null)
                    StatusChanged(this, EventArgs.Empty);
            }
        }
        public TaskConfig Config
        {
            get { return config; }
            set
            {
                this.config = value;
                config.Task = this;
                LoadLogger();
            }
        }

        public int RunningJobCount { get; protected set; }

        public string LogMsg
        {
            get
            {
                return sbLog.ToString();
            }
        }

        public string StatusSummary
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine(string.Format(TASK_STATUS_FORMAT, Status, startTime.ToString(), RunningJobCount));
                lock (runningJobs)
                {
                    Dictionary<ISpiderMan, int>.Enumerator erator = runningJobs.GetEnumerator();
                    while (erator.MoveNext())
                    {
                        sb.AppendLine(string.Format("Job Type: {0} Instance count: {1}\r\nSummary: {2}",
                             erator.Current.Key.GetType().ToString(), erator.Current.Value,
                             erator.Current.Key.StatusSummary));
                    }
                }
                return sb.ToString();
            }
        }

        public Task()
        {
            Config = new TaskConfig();
            runningJobs = new Dictionary<ISpiderMan, int>();
        }

        public void Start()
        {
            if (Status == JobStatus.Unstarted
                || Status == JobStatus.Stopped)
            {
                Status = JobStatus.Running;

                if (scheduler == null)
                {
                    startTime = DateTime.Now;
                    scheduler = schedulerFactory.GetScheduler();
                    JobDetail detail = Config.GetCurrentJob();
                    if (detail != null)
                    {
                        detail.JobDataMap.Add(Task.TAKS_ENTITY, this);
                        detail.JobDataMap.Add(Task.TAKS_LOGGER, logger);
                        scheduler.ScheduleJob(detail, new SimpleTrigger(detail.Name, null));

                        sbLog = new StringBuilder();
                        Log.Info(logger, "Task {0} Started!",config.TaskName);
                        scheduler.Start();
                        
                    }
                }
            }
        }
        public void Stop()
        {
            if (scheduler != null && 
                (Status == JobStatus.Running || Status == JobStatus.Completing))
            {
                Thread t = new Thread(Stopping);
                t.Start();
            }
            
        }

        protected void Stopping()
        {
            Status = JobStatus.Stopping;
            Log.Info(logger, "Task {0} Stopping", config.TaskName);

            lock (runningJobs)
            {
                Dictionary<ISpiderMan, int>.Enumerator erator = runningJobs.GetEnumerator();
                while (erator.MoveNext())
                {
                    erator.Current.Key.Interrupt();
                }
            }

            while (RunningJobCount > 0)
            {
                Thread.Sleep(1000);
            }
            Log.Info(logger, "Task {0} Stopped", config.TaskName);

            scheduler.Shutdown();
            Status = JobStatus.Stopped;
            scheduler = null;
        }



        public void NotifiyExcuting(ISpiderMan job)
        {
            lock (runningJobs)
            {
                if (runningJobs.ContainsKey(job))
                {
                    runningJobs[job]++;
                }
                else runningJobs.Add(job, 1);
            }
            RunningJobCount++;
        }

        public void NotifiyExcuted(ISpiderMan job)
        {
            lock (runningJobs)
            {
                if (runningJobs.ContainsKey(job))
                {
                    if (--runningJobs[job] == 0)
                    {
                        runningJobs.Remove(job);
                    }
                }
            }
            if (RunningJobCount > 0) RunningJobCount--;
           
        }

        protected void LoadLogger()
        {
            if (config != null && config.TaskName!=null)
            {
                logger = log4net.LogManager.GetLogger(config.TaskName);
                HandleLogMsg();
            }
        }

        protected virtual void HandleLogMsg()
        {
            if (log4net.Appender.FireEventAppender.Instance == null) return;
            if (!hasHandledLog && config != null && config.MemoryLogSize >= TaskConfig.MINI_MEM_LOG_SIZE)
            {
                Debug.WriteLine("Handled Logger!");
                    log4net.Appender.FireEventAppender.Instance.MessageLoggedEvent += new log4net.Appender.MessageLoggedEventHandler(Instance_MessageLoggedEvent);
                    hasHandledLog = true;
            }
            else if (hasHandledLog && config != null && config.MemoryLogSize < TaskConfig.MINI_MEM_LOG_SIZE)
            {
                log4net.Appender.FireEventAppender.Instance.MessageLoggedEvent -= new log4net.Appender.MessageLoggedEventHandler(Instance_MessageLoggedEvent);
                hasHandledLog = false;
            }
        }

        protected void Instance_MessageLoggedEvent(object sender, log4net.Appender.MessageLoggedEventArgs e)
        {
            if (logger != null && e.LoggingEvent.LoggerName == logger.Logger.Name)
            {
                if (MessageLogged != null) MessageLogged(this, e);
                //Debug.WriteLine("Catch: " + e.LoggingEvent.RenderedMessage);

                sbLog.AppendLine(e.LoggingEvent.RenderedMessage);
                if (config != null && sbLog.Length > config.MemoryLogSize)
                {
                    lock (sbLog)
                    {
                        string temp = sbLog.ToString().Substring(sbLog.Length / 2);
                        sbLog = new StringBuilder();
                        sbLog.AppendLine(temp);
                    }
                }
            }

        }


        #region TaskListener
        public class TaskListener : JobListenerSupport
        {
            Task task = null;
            public TaskListener(Task task)
            {
                this.task = task;
            }

            public override void JobWasExecuted(JobExecutionContext context, JobExecutionException jobException)
            {
                base.JobWasExecuted(context, jobException);
                task.Status = JobStatus.Completed;
            }


            public override string Name
            {
                get { return "Task Listener"; }
            }
        } 
        #endregion


        public void NotifyComplete()
        {
            if (RunningJobCount == 0 && Status == JobStatus.Running)
            {
                Stop();
            }
        }
    }
}
